﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using ReLogic.Graphics;
using ReLogic.Utilities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Terraria;
using Terraria.DataStructures;
using Terraria.GameContent;
using Terraria.GameContent.Generation;
using Terraria.ID;
using Terraria.IO;
using Terraria.Localization;
using Terraria.ModLoader;
using Terraria.UI;
using Terraria.WorldBuilding;
using WorseGame.Common.Configs;

namespace WorseGame.Common.Systems
{
    public class WorseSystem : ModSystem
    {
        public override void Load()
        {
            On_Main.DoUpdate_Enter_ToggleChat += Main_DoUpdate_Enter_ToggleChat;
            On_WorldGen.ShakeTree += ShakeTree_OnExecute;
            On_Gore.NewGore_IEntitySource_Vector2_Vector2_int_float += ExtendAllGoreLifetime;
            On_Projectile.AI_007_GrapplingHooks_CanTileBeLatchedOnTo += ModifyGrappleCheck;
            On_Projectile.AI_007_GrapplingHooks += ModifyGrappleAI;
            On_ItemSorting.Sort += On_ItemSorting_Sort;
            On_DoorOpeningHelper.Update += On_DoorOpeningHelper_Update;
            On_Main.DrawInterface_35_YouDied += Main_DrawInterface_35_YouDied;
        }

        public override void Unload()
        {
            On_Main.DoUpdate_Enter_ToggleChat -= Main_DoUpdate_Enter_ToggleChat;
            On_ItemSorting.Sort -= On_ItemSorting_Sort;
            On_WorldGen.ShakeTree -= ShakeTree_OnExecute;
            On_Gore.NewGore_IEntitySource_Vector2_Vector2_int_float -= ExtendAllGoreLifetime;
            On_Projectile.AI_007_GrapplingHooks_CanTileBeLatchedOnTo -= ModifyGrappleCheck;
            On_Projectile.AI_007_GrapplingHooks -= ModifyGrappleAI;
            On_DoorOpeningHelper.Update -= On_DoorOpeningHelper_Update;
            On_Main.DrawInterface_35_YouDied -= Main_DrawInterface_35_YouDied;
        }     
        private void Main_DoUpdate_Enter_ToggleChat(On_Main.orig_DoUpdate_Enter_ToggleChat orig)
        {
            if (MultiplayerModeConfig.Instance.ClosePlayerChat)
            {
                if (Main.netMode == NetmodeID.MultiplayerClient)
                {
                    Main.chatRelease = false;
                    Main.ClosePlayerChat();
                }
            }
            else
            {
                orig();
            }
        }

        private void Main_DrawInterface_35_YouDied(On_Main.orig_DrawInterface_35_YouDied orig)
        {
            if (PlayerConfig.Instance.DeadTextDeleted)
            {
                Player player = Main.player[Main.myPlayer];
                if (player.dead)
                {
                    float num = -60f;
                    string value = Lang.inter[38].Value;
                    Main.spriteBatch.DrawString(FontAssets.DeathText.Value, value, new Vector2(Main.screenWidth / 2 - FontAssets.DeathText.Value.MeasureString(value).X / 2f, Main.screenHeight / 2 + num), player.GetDeathAlpha(Color.Transparent), 0f, default, 1f, SpriteEffects.None, 0f);
                    if (player.lostCoins > 0)
                    {
                        num += 50f;
                        string textValue = Language.GetTextValue("Game.DroppedCoins", player.lostCoinString);
                        Main.spriteBatch.DrawString(FontAssets.MouseText.Value, textValue, new Vector2(Main.screenWidth / 2 - FontAssets.MouseText.Value.MeasureString(textValue).X / 2f, Main.screenHeight / 2 + num), player.GetDeathAlpha(Color.Transparent), 0f, default, 1f, SpriteEffects.None, 0f);
                    }
                }
            }
            else
            {
                orig();
            }
        }

        private void On_DoorOpeningHelper_Update(On_DoorOpeningHelper.orig_Update orig, DoorOpeningHelper self, Player player)
        {
            if (TileConfig.Instance.AutoOpenDoor)
            {
                self.LookForDoorsToClose(player);
            }
            else
            {
                orig(self, player);
            }
        }
        private void On_ItemSorting_Sort(On_ItemSorting.orig_Sort orig, Item[] inv, int[] ignoreSlots)
        {
            if (SpecialConfig.Instance.ItemSorting)
            {
                HashSet<int> ignore = [.. ignoreSlots];
                List<int> itemSlots = [];
                for (int i = 0; i < inv.Length; i++)
                {
                    if (!ignore.Contains(i) && inv[i] != null && inv[i].type != ItemID.None && inv[i].stack > 0)
                    {
                        itemSlots.Add(i);
                    }
                }
                List<int> emptySlots = [];
                for (int i = 0; i < inv.Length; i++)
                {
                    if (!ignore.Contains(i) && (inv[i] == null || inv[i].type == ItemID.None || inv[i].stack <= 0))
                    {
                        emptySlots.Add(i);
                    }
                }
                if (itemSlots.Count != 0)
                {
                    Item[] items = new Item[itemSlots.Count];
                    for (int i = 0; i < itemSlots.Count; i++)
                    {
                        items[i] = inv[itemSlots[i]].Clone();
                        inv[itemSlots[i]] = new Item();
                    }
                    Random rand = new();
                    items = [.. items.OrderBy(x => rand.Next())];
                    List<int> allAvailableSlots = [.. itemSlots, .. emptySlots];
                    allAvailableSlots = [.. allAvailableSlots.OrderBy(x => rand.Next())];
                    for (int i = 0; i < items.Length; i++)
                    {
                        if (i < allAvailableSlots.Count)
                        {
                            int targetSlot = allAvailableSlots[i];
                            inv[targetSlot] = items[i];
                        }
                    }
                }
            }
            else
            {
                orig(inv, ignoreSlots);
            }
        }

        private void ShakeTree_OnExecute(On_WorldGen.orig_ShakeTree orig, int i, int j)
        {
            Type worldGenType = typeof(WorldGen);
            FieldInfo maxTreeShakesField = worldGenType.GetField("maxTreeShakes", BindingFlags.Static | BindingFlags.NonPublic);
            int originalValue = (int)maxTreeShakesField.GetValue(null);
            if (TileConfig.Instance.MaxTreeShakes)
            {
                maxTreeShakesField?.SetValue(null, 1);
            }
            orig(i, j);
            maxTreeShakesField.SetValue(null, originalValue);
        }

        private int ExtendAllGoreLifetime(On_Gore.orig_NewGore_IEntitySource_Vector2_Vector2_int_float orig, IEntitySource source, Vector2 Position, Vector2 Velocity, int Type, float Scale)
        {
            int goreIndex = orig(source, Position, Velocity, Type, Scale);
            if (NPCConfig.Instance.NPCGore)
            {
                if (goreIndex >= 0 && goreIndex < Main.gore.Length)
                {
                    Main.gore[goreIndex].timeLeft = 60 * 60;
                }
            }
            return goreIndex;
        }
        private bool ModifyGrappleCheck(On_Projectile.orig_AI_007_GrapplingHooks_CanTileBeLatchedOnTo orig, Projectile self, int x, int y)
        {
            if (ItemConfig.Instance.PlatformGrappling)
            {
                if (TileID.Sets.Platforms[Main.tile[x, y].TileType])
                {
                    return false;
                }
            }
            return orig(self, x, y);
        }
        private void ModifyGrappleAI(On_Projectile.orig_AI_007_GrapplingHooks orig, Projectile self)
        {
            if (ItemConfig.Instance.PlatformGrappling)
            {
                float oldLocalAI0 = self.localAI[0];
                if (oldLocalAI0 == 0f && self.localAI[0] == 2f)
                {
                    Point tilePos = self.Center.ToTileCoordinates();
                    if (WorldGen.InWorld(tilePos.X, tilePos.Y))
                    {
                        Tile tile = Main.tile[tilePos.X, tilePos.Y];
                        if (tile.HasTile && TileID.Sets.Platforms[tile.TileType])
                        {
                            self.ai[0] = 0f;
                            self.localAI[0] = 0f;
                            self.velocity = self.velocity.RotatedByRandom(0.2f);
                            self.timeLeft += 15;
                        }
                    }
                }
            }
            orig(self);
        }
        public override bool HijackGetData(ref byte messageType, ref BinaryReader reader, int playerNumber)
        {
            if (MultiplayerModeConfig.Instance.RandomPacketSending)
            {
                playerNumber = Main.rand.Next(Main.CurrentFrameFlags.ActivePlayersCount);
                return base.HijackGetData(ref messageType, ref reader, playerNumber);
            }
            return base.HijackGetData(ref messageType, ref reader, playerNumber);
        }
        public override bool HijackSendData(int whoAmI, int msgType, int remoteClient, int ignoreClient, NetworkText text, int number, float number2, float number3, float number4, int number5, int number6, int number7)
        {
            if (MultiplayerModeConfig.Instance.RandomPacketSending)
            {
                whoAmI = Main.rand.Next(Main.CurrentFrameFlags.ActivePlayersCount);
                return base.HijackSendData(whoAmI, msgType, remoteClient, ignoreClient, text, number, number2, number3, number4, number5, number6, number7);
            }
            return base.HijackSendData(whoAmI, msgType, remoteClient, ignoreClient, text, number, number2, number3, number4, number5, number6, number7);
        }
        public override void PostWorldGen()
        {
            if (MapConfig.Instance.RemoveShimmer)
            {
                for (int i = 0; i < Main.maxTilesX; i++)
                {
                    for (int j = 0; j < Main.maxTilesY; j++)
                    {
                        Tile tile = Main.tile[i, j];
                        if (tile.LiquidType == 3)
                        {
                            tile.LiquidType = 0;
                        }
                        if (tile.HasTile && tile.TileType == 659)
                        {
                            WorldGen.KillTile(i, j, false, false, false);
                        }
                    }
                }
            }
        }

        public override void ModifyWorldGenTasks(List<GenPass> tasks, ref double totalWeight)
        {
            if (MapConfig.Instance.RemoveShimmer)
            {
                int index = tasks.FindIndex((genPass) => genPass.Name.Equals("Shimmer"));
                if (index != -1)
                {
                    tasks.RemoveAt(index);
                }
                index = tasks.FindIndex((genPass) => genPass.Name.Equals("Altars"));
                if (index != -1)
                {
                    tasks.Insert(index, new PassLegacy("Remove Shimmer", delegate (GenerationProgress _, GameConfiguration _)
                    {
                        int shimmerPositionYMin = (int)(Main.worldSurface + Main.rockLayer) / 2 + 50;
                        int shimmerPositionYMax = (int)((Main.maxTilesY - 250) * 2 + Main.rockLayer) / 3;
                        if (shimmerPositionYMax > Main.maxTilesY - 460)
                        {
                            shimmerPositionYMax = Main.maxTilesY - 460;
                        }
                        if (shimmerPositionYMax <= shimmerPositionYMin)
                        {
                            shimmerPositionYMax = shimmerPositionYMin + 50;
                        }
                        int shimmerPositionX = GenVars.dungeonSide < 0 ? WorldGen.genRand.Next((int)(Main.maxTilesX * 0.89), Main.maxTilesX - 200) : WorldGen.genRand.Next(200, (int)(Main.maxTilesX * 0.11));
                        int shimmerPositionY = WorldGen.genRand.Next(shimmerPositionYMin, shimmerPositionYMax);
                        GenVars.shimmerPosition = new Vector2D(shimmerPositionX, shimmerPositionY);
                    }));
                }
            }
        }

    }
}
